%include {

/** @brief Lemon parser grammar for Turtle.
 *
 * The `lemon' parser generator executable must be in your PATH:
 * https://sqlite.org/src/doc/trunk/doc/lemon.html
 *
 * TTL EBNF: https://www.w3.org/TeamSubmission/turtle/#sec-grammar-grammar
 */

#include "codec.h"

}

%name TTLParse

%stack_overflow {
    log_error ("Stack oveflow in TTL parsing.");
    state->rc = LSUP_MEM_ERR;
}

%parse_failure {
    log_error ("TTL parse error. Cannot continue.");
    state->rc = LSUP_PARSE_ERR;
}

%syntax_error {
    // Fail immediately on first error.
    yy_parse_failed (yypParser);
}

//%stack_size 1024

%token_prefix "T_"
%token_type { char * }
%token_destructor { (void) state; free ($$); }

%default_type           { char * }

%extra_argument         { LSUP_TTLParserState *state }

// Low- to high-priority.
%left PERIOD .
%left SEMICOLON .
%left COMMA .
%left STRING INTEGER DOUBLE DECIMAL BOOLEAN QNAME BNODE_ID IRIREF .
%nonassoc LANGTAG PREFIX .
%nonassoc COLON .

/*
 * Rules.
 */

turtleDoc   ::= statements EOF .
statements  ::= statements statement .
statements  ::= .

statement 	::= prefixID .
statement   ::= base .
statement   ::= triples .
statement   ::= WS .

prefixID    ::= PREFIX(P) WS IRIREF(N) PERIOD . {
                LSUP_nsmap_add (
                    LSUP_graph_namespace (
                        LSUP_graph_iter_graph (state->it)
                    ), P, N
                );

                free (P);
                free (N);
            }

base        ::= BASE WS IRIREF(D) PERIOD . {
                LSUP_term_free (state->base);
                state->base = LSUP_iriref_new (D, NULL);

                free (D);
            }

triples 	::= subject(S) ows predObjList(L) PERIOD . {
                size_t ct = LSUP_graph_add_link_map (state->it, S, L);
                state->ct += ct;
                state->rc = LSUP_OK;
                log_trace ("Added %lu triples.", ct);

                LSUP_term_free (S);
                LSUP_link_map_free (L);
            }
triples 	::= subject(S) ows predObjList(L) SEMICOLON PERIOD . [PERIOD] {
                size_t ct = LSUP_graph_add_link_map (state->it, S, L);
                state->ct += ct;
                state->rc = LSUP_OK;
                log_trace ("Added %lu triples.", ct);

                LSUP_term_free (S);
                LSUP_link_map_free (L);
            }

%type predObjList       { LSUP_LinkMap * }
%destructor predObjList { LSUP_link_map_free ($$); }
predObjList(A) ::= predicate(P) ows objectList(O) . [SEMICOLON] {
                A = LSUP_link_map_new (LSUP_LINK_OUTBOUND);
                LSUP_link_map_add (A, P, O);
            }
predObjList(A) ::= predObjList(L) SEMICOLON predicate(P) ows objectList(O) . {
                LSUP_link_map_add (L, P, O);
                A = L;
            }

%type objectList { LSUP_TermSet * }
%destructor objectList { LSUP_term_set_free ($$); }
objectList(A) ::= objectList(L) COMMA object(O) . {
                if (LSUP_term_set_add (L, O, NULL) == LSUP_NOACTION)
                    LSUP_term_free (O);
                A = L;
            }
objectList(A) ::= object(O) . [IRIREF] {
                A = LSUP_term_set_new();
                LSUP_term_set_add (A, O, NULL);
            }

%type subject { LSUP_Term * }
%destructor subject { LSUP_term_free ($$); }
subject 	::= resource .
subject 	::= blank .

%type predicate { LSUP_Term * }
%destructor predicate { LSUP_term_free ($$); }
predicate   ::= resource .
predicate(A)::= RDF_TYPE . {
                A = LSUP_iriref_new (
                        "rdf:type",
                        LSUP_graph_namespace (LSUP_graph_iter_graph (state->it)));
                }

%type object { LSUP_Term * }
%destructor object { LSUP_term_free ($$); }
object 	    ::= resource .
object 	    ::= blank .
object 	    ::= literal .

%type literal { LSUP_Term * }
%destructor literal { LSUP_term_free ($$); }
literal(A)  ::= STRING(D) . {
                A = LSUP_term_new (LSUP_TERM_LITERAL, D, NULL);
                log_trace ("Created plain literal: \"%s\"", A->data);
                free (D);
            }
literal(A)  ::= STRING(D) LANGTAG(L) . {
                A = LSUP_term_new (LSUP_TERM_LT_LITERAL, D, L);
                log_trace ("Created LT-literal: \"%s\"@%s", A->data, A->lang);
                free (D);
                free (L);
            }
literal(A)  ::= STRING(D) DTYPE_MARKER resource(M) . {
                A = LSUP_term_new (LSUP_TERM_LITERAL, D, M);
                log_trace (
                        "Created DT-literal: \"%s\"^^%s",
                        A->data, A->datatype);
                free (D);
            }
literal(A)  ::= INTEGER(D) . {
                A = LSUP_term_new (
                    LSUP_TERM_LITERAL, D,
                    LSUP_iriref_new (
                        "xsd:integer", 
                        LSUP_graph_namespace (LSUP_graph_iter_graph (state->it))
                    )
                );

                free (D);
            }
literal(A)  ::= DOUBLE(D) . {
                A = LSUP_term_new (
                    LSUP_TERM_LITERAL, D,
                    LSUP_iriref_new (
                        "xsd:double", 
                        LSUP_graph_namespace (LSUP_graph_iter_graph (state->it))
                    )
                );
                free (D);
            }
literal(A)  ::= DECIMAL(D) . {
                A = LSUP_term_new (
                    LSUP_TERM_LITERAL, D,
                    LSUP_iriref_new (
                        "xsd:decimal", 
                        LSUP_graph_namespace (LSUP_graph_iter_graph (state->it))
                    )
                );
                free (D);
            }
literal(A)  ::= BOOLEAN(D) . {
                A = LSUP_term_new (
                    LSUP_TERM_LITERAL, D,
                    LSUP_iriref_new (
                        "xsd:boolean", 
                        LSUP_graph_namespace (LSUP_graph_iter_graph (state->it))
                    )
                );
                free (D);
            }

%type blank { LSUP_Term * }
%destructor blank { LSUP_term_free ($$); }
blank(A)    ::= BNODE_ID(D) . {
                A = LSUP_term_new (LSUP_TERM_BNODE, D, NULL);
                log_trace ("Created blank node: _:%s", A->data);

                free (D);
            }
blank(A)    ::= LBRACKET RBRACKET . [BNODE_ID] {
                A = LSUP_term_new (LSUP_TERM_BNODE, NULL, NULL);
                log_trace ("Created empty list BN: _:%s", A->data);
            }
blank(A)    ::= LBRACKET predObjList(L) RBRACKET . [BNODE_ID] {
                A = LSUP_term_new (LSUP_TERM_BNODE, NULL, NULL);
                state->ct += LSUP_graph_add_link_map (state->it, A, L);
                log_trace ("Created list BN: _:%s", A->data);

                LSUP_link_map_free (L);
            }
blank       ::= collection . [BNODE_ID]
blank(A)    ::= LPAREN RPAREN . [BNODE_ID] {
                A = LSUP_iriref_new (
                    "rdf:nil",
                    LSUP_graph_namespace (LSUP_graph_iter_graph (state->it))
                );
                log_trace ("Created list terminator: %s", A->data);
            }

// "collection" is the subject of the first collection item.
%type collection { LSUP_Term * }
%destructor collection { LSUP_term_free ($$); }
// Collection triples are added here to the graph.
collection(A) ::= LPAREN itemList(L) RPAREN . {
                A = LSUP_bnode_add_collection (state->it, L);
                LSUP_term_set_free (L);
            }

%type itemList { LSUP_TermSet * }
%destructor itemList { LSUP_term_set_free ($$); }
itemList(A) ::= itemList(L) ows object(O) . {
                if (LSUP_term_set_add (L, O, NULL) == LSUP_NOACTION)
                    LSUP_term_free (O);
                A = L;
            }
itemList(A) ::= object(O) . {
                A = LSUP_term_set_new ();
                LSUP_term_set_add (A, O, NULL);
            }

%type resource { LSUP_Term * }
%destructor resource { LSUP_term_free ($$); }
resource(A) ::= IRIREF(D) . {
                LSUP_Term *rel_iri = LSUP_iriref_new (D, NULL);
                free (D);
                if (state->base) {
                    A = LSUP_iriref_absolute (rel_iri, state->base);
                    LSUP_term_free (rel_iri);
                } else {
                    A = rel_iri;
                }
                log_trace ("Created IRI: <%s>", A->data);
            }
resource(A) ::= QNAME(D) . {
                A = LSUP_iriref_new (
                    D, LSUP_graph_namespace (LSUP_graph_iter_graph (state->it))
                );
                log_trace ("Created IRI: %s", A->data);
                free (D);
            }

ows         ::= WS .
ows         ::= .
